/*-------------------------------------------------------------------------
 *
 * win32env.c
 *	  为 win32 提供 putenv()、setenv() 和 unsetenv()。
 *
 * 这些函数同时更新进程环境和 (可能是多个) C 运行时库 (CRT) 版本中的缓存。
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/port/win32env.c
 *
 *-------------------------------------------------------------------------
 */

#include "c.h"


/*
 * 请注意，与 POSIX putenv() 不同，这不会使用传入的字符串作为永久存储。
 */
int pgwin32_putenv(const char *envval)
{
	char	   *envcpy;
	char	   *cp;
	typedef int (_cdecl * PUTENVPROC) (const char *);
	static const char *const modulenames[] = {
		"msvcrt",				/* Visual Studio 6.0 / MinGW */
		"msvcrtd",
		"msvcr70",				/* Visual Studio 2002 */
		"msvcr70d",
		"msvcr71",				/* Visual Studio 2003 */
		"msvcr71d",
		"msvcr80",				/* Visual Studio 2005 */
		"msvcr80d",
		"msvcr90",				/* Visual Studio 2008 */
		"msvcr90d",
		"msvcr100",				/* Visual Studio 2010 */
		"msvcr100d",
		"msvcr110",				/* Visual Studio 2012 */
		"msvcr110d",
		"msvcr120",				/* Visual Studio 2013 */
		"msvcr120d",
		"ucrtbase",				/* Visual Studio 2015 及以后版本 */
		"ucrtbased",
		NULL
	};
	int			i;

	/*
	 * 更新进程环境，使此更改对子进程和将来初始化的 CRT 可见。在 _putenv() 循环之前执行此操作，以便任何在此 pgwin32_putenv() 执行期间初始化的 CRT 都能受益，在循环检查该 CRT 之后。
	 *
	 * 需要字符串的副本，以便我们可以修改它。
	 */
	envcpy = strdup(envval);
	if (!envcpy)
		return -1;
	cp = strchr(envcpy, '=');
	if (cp == NULL)
	{
		free(envcpy);
		return -1;
	}
	*cp = '\0';
	cp++;
	if (*cp)
	{
		/*
		 * 仅在添加变量时调用 SetEnvironmentVariable()，而不是在删除时。对两个都调用至少会在某些版本的 MinGW 上崩溃。
		 */
		if (!SetEnvironmentVariable(envcpy, cp))
		{
			free(envcpy);
			return -1;
		}
	}
	free(envcpy);

	/*
	 * 每个 CRT 都有自己的 _putenv() 符号和环境副本。
	 * 更新当前加载的每个 CRT 模块中的环境，以便每个第三方库都能看到此更改，而不管它链接的 CRT 是什么。我们调用 FreeLibrary() 的瞬间，这些模块中的地址可能会变为无效，因此不要缓存它们。
	 */
	for (i = 0; modulenames[i]; i++)
	{
		HMODULE		hmodule = NULL;
		BOOL		res = GetModuleHandleEx(0, modulenames[i], &hmodule);

		if (res != 0 && hmodule != NULL)
		{
			PUTENVPROC	putenvFunc;

			putenvFunc = (PUTENVPROC) (pg_funcptr_t) GetProcAddress(hmodule, "_putenv");
			if (putenvFunc)
				putenvFunc(envval);
			FreeLibrary(hmodule);
		}
	}

	/*
	 * 最后，更新我们的“自有”缓存。这与上面的循环是多余的，除非 PostgreSQL 本身链接到未列出的 CRT。理想情况下，循环确实访问所有可能的 CRT，使此操作多余。
	 */
	return _putenv(envval);
}

int pgwin32_setenv(const char *name, const char *value, int overwrite)
{
	int			res;
	char	   *envstr;

	/* 错误条件，符合 POSIX */
	if (name == NULL || name[0] == '\0' || strchr(name, '=') != NULL ||
		value == NULL)
	{
		errno = EINVAL;
		return -1;
	}

	/* 如果变量存在且我们不替换它，则不执行操作 */
	if (overwrite == 0 && getenv(name) != NULL)
		return 0;

	envstr = (char *) malloc(strlen(name) + strlen(value) + 2);
	if (!envstr)				/* 如果没有内存，我们能做的也不多 */
		return -1;

	sprintf(envstr, "%s=%s", name, value);

	res = pgwin32_putenv(envstr);
	free(envstr);
	return res;
}

int pgwin32_unsetenv(const char *name)
{
	int			res;
	char	   *envbuf;

	envbuf = (char *) malloc(strlen(name) + 2);
	if (!envbuf)
		return -1;

	sprintf(envbuf, "%s=", name);
	res = pgwin32_putenv(envbuf);
	free(envbuf);
	return res;
}
